package cn.edu.lcu.cs.javaprogramming.annotations;

import java.lang.reflect.Field;
import java.util.Arrays;

/**
 * 范围注解的检验器
 *
 * @author ling
 * @date 2022/5/7 21:48
 */
public class RangeValidator {
    /**
     * 验证目标对象的某些字段是否符合Range注解的标注范围。
     * 使用传统方法实现字段值的验证逻辑。
     *
     * <p>该方法会检查目标对象中带有@Range注解的字段，确保其值在指定的范围内。
     * 若字段值超出范围或字段类型不是数值类型，则验证失败。</p>
     *
     * @param target 需要验证的目标对象（加了@Range注解的对象）
     * @return 如果所有带@Range注解的字段都满足条件，返回true；否则返回false
     * @throws IllegalAccessException 如果访问私有字段时出现错误
     * @deprecated 推荐使用validate()方法替代此实现
     */
    @Deprecated
    public static boolean validate1(Object target) throws IllegalAccessException {
        boolean isValid = false;

        if (target != null) {
            // 获取类的所有声明字段
            Field[] fields = target.getClass().getDeclaredFields();

            // 遍历每个字段进行处理
            for (Field field : fields) {
                // 尝试获取字段上的@Range注解
                Range annotation = field.getAnnotation(Range.class);

                // 如果字段有@Range注解
                if (annotation != null) {
                    // 设置字段为可访问
                    field.setAccessible(true);

                    // 获取注解定义的最小和最大值
                    long min = annotation.min();
                    long max = annotation.max();

                    // 获取字段的实际值
                    Object value = field.get(target);

                    // 检查值是否为数值类型
                    if (value instanceof Number) {
                        int intValue = ((Number) value).intValue();

                        // 检查数值是否在允许范围内
                        if (intValue <= max && intValue >= min) {
                            isValid = true;
                        } else {
                            // 数值不在范围内，验证失败
                            isValid = false;
                            break;
                        }
                    } else {
                        // 字段不是数值类型，验证失败
                        isValid = false;
                        break;
                    }
                }
            }
        }

        return isValid;
    }

    /**
     * 验证目标对象的某些字段是否符合Range注解的标注范围。
     * 使用Stream API实现更简洁的验证逻辑。
     *
     * <p>该方法会检查目标对象中带有@Range注解的字段，确保其值在指定的范围内。
     * 若字段值超出范围或字段类型不是数值类型，或者发生异常，则验证失败。</p>
     *
     * @param target 需要验证的目标对象（加了@Range注解的对象）
     * @return 如果所有带@Range注解的字段都满足条件，返回true；否则返回false
     */
    public static boolean validate(Object target) {
        boolean isValid = false;

        if (target != null) {
            // 获取类的所有声明字段
            Field[] fields = target.getClass().getDeclaredFields();

            // 使用Stream处理字段集合
            isValid = Arrays.stream(fields)
                    // 过滤出包含@Range注解的字段
                    .filter(field -> field.isAnnotationPresent(Range.class))
                    // 检查是否所有字段都满足条件
                    .allMatch(field -> {
                        // 设置字段为可访问
                        field.setAccessible(true);
                        try {
                            // 获取字段的实际值
                            Object valueObject = field.get(target);

                            // 检查值是否为数值类型
                            if (valueObject instanceof Number) {
                                // 获取整型值
                                int intValue = ((Number) valueObject).intValue();

                                // 获取字段上的@Range注解
                                Range annotation = field.getAnnotation(Range.class);

                                // 获取注解定义的最小和最大值
                                int max = annotation.max();
                                int min = annotation.min();

                                // 检查数值是否在允许范围内
                                if (min <= intValue && intValue <= max) {
                                    return true;
                                }
                            }
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }

                        // 返回false表示以下情况之一：
                        // 1. 字段不是数值类型
                        // 2. 数值不在指定范围内
                        // 3. 访问字段时发生异常
                        return false;
                    });
        }

        return isValid;
    }
}